#pragma once

#include "Util.hpp"
#include <unordered_map>

enum session_status
{
    UNLOGIN,
    LOGIN
};

class session
{
public:
    session(uint32_t ssid)
        : _ssid(ssid)
        , _status(UNLOGIN)
    {
        LOG(NORMAL, "session:%p 被创建", this);
    }
    ~session() { LOG(NORMAL, "session:%p 被释放", this); }

    void set_user(uint32_t uid) { _uid = uid; }
    uint32_t get_user() { return _uid; }
    uint32_t ssid() { return _ssid; }
    bool is_login() { return _status == LOGIN; }
    void set_login(session_status status = LOGIN) { _status = status; }
    void set_timer(const webserver_t::timer_ptr& time_ptr) { _time_ptr = time_ptr; }
    webserver_t::timer_ptr& get_timer() { return _time_ptr; }
    
private:
    uint32_t _ssid; // session ID
    uint32_t _uid;  // UID 
    session_status _status; // session 状态
    webserver_t::timer_ptr _time_ptr; // 定时器
};

using session_ptr = std::shared_ptr<session>;

class session_manager
{    
public:
    const static time_t SESSION_FOREVER = -1;
    const static time_t SESSION_TIMEOUT = 30000;
public:

    session_manager(webserver_t* server)
        : _session_count(1)
        , _server(server)
    {}

    session_ptr create_session(uint32_t uid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
    
        session_ptr ssp(new session(_session_count));
        ssp->set_user(uid);
        ssp->set_login();
        _sessions.insert({_session_count, ssp});

        ++_session_count;

        return ssp;
    }

    session_ptr get_session_from_ssid(uint32_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        
        auto it = _sessions.find(ssid);
        if (it == _sessions.end())
        {
            LOG(DEBUG, "Not found ssid");
            return session_ptr(nullptr);
        }

        return it->second;
    }

    void erase_session(uint32_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _sessions.erase(ssid);
    }

    void set_session_expire_time(uint32_t ssid, time_t ms = SESSION_TIMEOUT)
    {
        auto ssp = get_session_from_ssid(ssid);
        if (ssp.get() == nullptr) // session 不存在
        {
            return;
        }

        auto tp = ssp->get_timer(); // 为空表示定时器为设置，即永久存在

        // 在 session 永久存在的情况下设置成永久存在
        if (tp.get() == nullptr && ms == SESSION_FOREVER)
        {
            return;
        }

        // 在 session 永久存在的情况下设置成 定时状态
        else if (tp.get() == nullptr && ms != SESSION_FOREVER)
        {
            webserver_t::timer_ptr tmp_timer_ptr = _server->set_timer(ms, [this, ssid](const std::error_code&) { erase_session(ssid); });
            ssp->set_timer(tmp_timer_ptr);
        }

        // 已经有 定时任务 将定时任务转换成永久存在
        else if (tp.get() != nullptr && ms == SESSION_FOREVER)
        {
            tp->cancel();
            ssp->set_timer(webserver_t::timer_ptr());

            // 防止任务直接被执行，重新添加任务
            _server->set_timer(0, [this, ssp](const std::error_code&) { 
                std::unique_lock<std::mutex> lock(_mutex);
                _sessions.insert({ssp->ssid(), ssp});
            });
        }

        // 已经有 定时任务 将定时任务 重新设置定时时间
        else if (tp.get() != nullptr && ms != SESSION_FOREVER)
        {
            tp->cancel();
            ssp->set_timer(webserver_t::timer_ptr());

            // 防止任务直接被执行，重新添加任务
            auto tmp_tp = _server->set_timer(ms, [this, ssp](const std::error_code&) { 
                std::unique_lock<std::mutex> lock(_mutex);
                _sessions.insert({ssp->ssid(), ssp});
            });

            ssp->set_timer(tmp_tp);
        }
    }


private:
    uint32_t _session_count;
    std::mutex _mutex;
    std::unordered_map<uint32_t, session_ptr> _sessions;
    webserver_t* _server; // 定时器
};